//
// RS Game Framework
// Copyright © 2010 Jedd Haberstro
// jhaberstro@gmail.com
// 
// $Id:
//

#ifndef RS_REFLECTION_CONSTRUCTOR_IPP
#define RS_REFLECTION_CONSTRUCTOR_IPP

#ifndef RS_REFLECTION_CONSTRUCTOR_IMPLEMENTATION_GUARD
#   error "Do not include this file directly. Instead, include rs/reflection/Constructor.hpp"
#endif

namespace rs
{
    namespace reflection
    {
        namespace internal
        {
            template< typename TClass >
            inline void* CreatorFunc(void* dest) {
                return new (dest) TClass;
            }

            template< typename TClass, typename TArg1 >
            inline void* CreatorFunc(void* dest, TArg1 arg1) {
                return new (dest) TClass(arg1);
            }

            template< typename TClass, typename TArg1, typename TArg2 >
            inline void* CreatorFunc(void* dest, TArg1 arg1, TArg2 arg2) {
                return new (dest) TClass(arg1, arg2);
            }

            template< typename TClass, typename TArg1, typename TArg2, typename TArg3 >
            inline void* CreatorFunc(void* dest, TArg1 arg1, TArg2 arg2, TArg3 arg3) {
                return new (dest) TClass(arg1, arg2, arg3);
            }

            template< typename TClass, typename TArg1, typename TArg2, typename TArg3, typename TArg4 >
            inline void* CreatorFunc(void* dest, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4) {
                return new (dest) TClass(arg1, arg2, arg3, arg4);
            }

            template< typename TClass, typename TArg1, typename TArg2, typename TArg3, typename TArg4, typename TArg5 >
            inline void* CreatorFunc(void* dest, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5) {
                return new (dest) TClass(arg1, arg2, arg3, arg4, arg5);
            }
            
            class ConstructorFactory
            {
            public:
                
                template< typename T >
                static Constructor Create() {
                    void* (*func)(void*) = &CreatorFunc< T >;
                    return Constructor(func, sizeof(T));
                }

                template< typename T, typename TArg1 >
                static Constructor Create() {
                    void* (*func)(void*, TArg1) = &CreatorFunc< T, TArg1 >;
                    Constructor::ArgumentTypesContainer types;
                    types.push_back(ConvertToQualifiedType< TArg1 >::kQualifiedType);
                    return Constructor(func, types, sizeof(T));
                }

                template< typename T, typename TArg1, typename TArg2 >
                static Constructor Create() {
                    void* (*func)(void*, TArg1, TArg2) = &CreatorFunc< T, TArg1, TArg2 >;
                    Constructor::ArgumentTypesContainer types;
                    types.push_back(ConvertToQualifiedType< TArg1 >::kQualifiedType);
                    types.push_back(ConvertToQualifiedType< TArg2 >::kQualifiedType);
                    return Constructor(func, types, sizeof(T));
                }

                template< typename T, typename TArg1, typename TArg2, typename TArg3 >
                static Constructor Create() {
                    void* (*func)(void*, TArg1, TArg2, TArg3) = &CreatorFunc< T, TArg1, TArg2, TArg3 >;
                    Constructor::ArgumentTypesContainer types;
                    types.push_back(ConvertToQualifiedType< TArg1 >::kQualifiedType);
                    types.push_back(ConvertToQualifiedType< TArg2 >::kQualifiedType);
                    types.push_back(ConvertToQualifiedType< TArg3 >::kQualifiedType);
                    return Constructor(func, types, sizeof(T));
                }

                template< typename T, typename TArg1, typename TArg2, typename TArg3, typename TArg4 >
                static Constructor Create() {
                    void* (*func)(void*, TArg1, TArg2, TArg3, TArg4) = &CreatorFunc< T, TArg1, TArg2, TArg3, TArg4 >;
                    Constructor::ArgumentTypesContainer types;
                    types.push_back(ConvertToQualifiedType< TArg1 >::kQualifiedType);
                    types.push_back(ConvertToQualifiedType< TArg2 >::kQualifiedType);
                    types.push_back(ConvertToQualifiedType< TArg3 >::kQualifiedType);
                    types.push_back(ConvertToQualifiedType< TArg4 >::kQualifiedType);
                    return Constructor(func, types, sizeof(T));
                }

                template< typename T, typename TArg1, typename TArg2, typename TArg3, typename TArg4, typename TArg5 >
                static Constructor Create() {
                    void* (*func)(void*, TArg1, TArg2, TArg3, TArg4, TArg5) = &CreatorFunc< T, TArg1, TArg2, TArg3, TArg4, TArg5 >;
                    Constructor::ArgumentTypesContainer types;
                    types.push_back(ConvertToQualifiedType< TArg1 >::kQualifiedType);
                    types.push_back(ConvertToQualifiedType< TArg2 >::kQualifiedType);
                    types.push_back(ConvertToQualifiedType< TArg3 >::kQualifiedType);
                    types.push_back(ConvertToQualifiedType< TArg4 >::kQualifiedType);
                    types.push_back(ConvertToQualifiedType< TArg5 >::kQualifiedType);
                    return Constructor(func, types, sizeof(T));
                }
            };
        }
        
        
        inline Constructor::Constructor()
        : classSize_(0) {
            creatorFunc_.fxn_ptr = 0;
        }
        
        template< typename TCreatorFunc >
        inline Constructor::Constructor(TCreatorFunc func, ArgumentTypesContainer const& argumentTypes, RsSize size)
        : argumentTypes_(argumentTypes),
          classSize_(size) {
            creatorFunc_.fxn_ptr = (reinterpret_cast< rs::internal::generic_fxn* >(func));
        }
        
        template< typename TCreatorFunc >
        inline Constructor::Constructor(TCreatorFunc func, RsSize size)
        : classSize_(size) {
            creatorFunc_.fxn_ptr = (reinterpret_cast< rs::internal::generic_fxn* >(func));
        }

        inline void* Constructor::operator()() const {
            RS_ASSERTF(argumentTypes_.size() == 0, "Provided argument count is incorrect. Should be: %i", argumentTypes_.size());
            typedef void* (*FunctionType) (void*);
            FunctionType func = reinterpret_cast< FunctionType >(creatorFunc_.fxn_ptr);
            void* obj = malloc(classSize_);
            return (*func)(obj);
        }

        inline void Constructor::operator()(void* obj) const {
            RS_ASSERT(obj != 0, "'obj' is null");
            RS_ASSERTF(argumentTypes_.size() == 0, "Provided argument count is incorrect. Should be: %i", argumentTypes_.size());
            typedef void* (*FunctionType) (void*);
            FunctionType func = reinterpret_cast< FunctionType >(creatorFunc_.fxn_ptr);
            (*func)(obj);
        }

        template< typename TArg1 >
        inline void* Constructor::operator()(TArg1 arg1) const {
            RS_ASSERTF(argumentTypes_.size() == 1, "Provided argument count is incorrect. Should be: %i", argumentTypes_.size());
            RS_ASSERT(argumentTypes_[0] == ConvertToQualifiedType< TArg1 >::kQualifiedType, "Wrong argument type");
            typedef void* (*FunctionType) (void*, TArg1);
            FunctionType func = reinterpret_cast< FunctionType >(creatorFunc_.fxn_ptr);
            void* obj = malloc(classSize_);
            return (*func)(obj, arg1);
        }

        template< typename TArg1 >
        inline void Constructor::operator()(void* obj, TArg1 arg1) const {
            RS_ASSERTF(argumentTypes_.size() == 1, "Provided argument count is incorrect. Should be: %i", argumentTypes_.size());
            RS_ASSERT(argumentTypes_[0] == ConvertToQualifiedType< TArg1 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(obj != 0, "'obj' is null");
            typedef void* (*FunctionType) (void*, TArg1);
            FunctionType func = reinterpret_cast< FunctionType >(creatorFunc_.fxn_ptr);
            (*func)(obj, arg1);
        }

        template< typename TArg1, typename TArg2 >
        inline void* Constructor::operator()(TArg1 arg1, TArg2 arg2) const {
            RS_ASSERTF(argumentTypes_.size() == 2, "Provided argument count is incorrect. Should be: %i", argumentTypes_.size());
            RS_ASSERT(argumentTypes_[0] == ConvertToQualifiedType< TArg1 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[1] == ConvertToQualifiedType< TArg2 >::kQualifiedType, "Wrong argument type");
            typedef void* (*FunctionType) (void*, TArg1, TArg2);
            FunctionType func = reinterpret_cast< FunctionType >(creatorFunc_.fxn_ptr);
            void* obj = malloc(classSize_);
            return (*func)(obj, arg1, arg2);
        }

        template< typename TArg1, typename TArg2 >
        inline void Constructor::operator()(void* obj, TArg1 arg1, TArg2 arg2) const {
            RS_ASSERTF(argumentTypes_.size() == 2, "Provided argument count is incorrect. Should be: %i", argumentTypes_.size());
            RS_ASSERT(argumentTypes_[0] == ConvertToQualifiedType< TArg1 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[1] == ConvertToQualifiedType< TArg2 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(obj != 0, "'obj' is null");
            typedef void* (*FunctionType) (void*, TArg1, TArg2);
            FunctionType func = reinterpret_cast< FunctionType >(creatorFunc_.fxn_ptr);
            (*func)(obj, arg1, arg2);
        }

        template< typename TArg1, typename TArg2, typename TArg3 >
        inline void* Constructor::operator()(TArg1 arg1, TArg2 arg2, TArg3 arg3) const {
            RS_ASSERTF(argumentTypes_.size() == 3, "Provided argument count is incorrect. Should be: %i", argumentTypes_.size());
            RS_ASSERT(argumentTypes_[0] == ConvertToQualifiedType< TArg1 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[1] == ConvertToQualifiedType< TArg2 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[2] == ConvertToQualifiedType< TArg3 >::kQualifiedType, "Wrong argument type");
            typedef void* (*FunctionType) (void*, TArg1, TArg2, TArg3);
            FunctionType func = reinterpret_cast< FunctionType >(creatorFunc_.fxn_ptr);
            void* obj = malloc(classSize_);
            return (*func)(obj, arg1, arg2, arg3);
        }

        template< typename TArg1, typename TArg2, typename TArg3 >
        inline void Constructor::operator()(void* obj, TArg1 arg1, TArg2 arg2, TArg3 arg3) const {
            RS_ASSERTF(argumentTypes_.size() == 3, "Provided argument count is incorrect. Should be: %i", argumentTypes_.size());
            RS_ASSERT(argumentTypes_[0] == ConvertToQualifiedType< TArg1 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[1] == ConvertToQualifiedType< TArg2 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[2] == ConvertToQualifiedType< TArg3 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(obj != 0, "'obj' is null");
            typedef void* (*FunctionType) (void*, TArg1, TArg2, TArg3);
            FunctionType func = reinterpret_cast< FunctionType >(creatorFunc_.fxn_ptr);
            (*func)(obj, arg1, arg2, arg3);
        }

        template< typename TArg1, typename TArg2, typename TArg3, typename TArg4 >
        inline void* Constructor::operator()(TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4) const {
            RS_ASSERTF(argumentTypes_.size() == 4, "Provided argument count is incorrect. Should be: %i", argumentTypes_.size());
            RS_ASSERT(argumentTypes_[0] == ConvertToQualifiedType< TArg1 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[1] == ConvertToQualifiedType< TArg2 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[2] == ConvertToQualifiedType< TArg3 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[3] == ConvertToQualifiedType< TArg4 >::kQualifiedType, "Wrong argument type");
            typedef void* (*FunctionType) (void*, TArg1, TArg2, TArg3, TArg4);
            FunctionType func = reinterpret_cast< FunctionType >(creatorFunc_.fxn_ptr);
            void* obj = malloc(classSize_);
            return (*func)(obj, arg1, arg2, arg3, arg4);
        }

        template< typename TArg1, typename TArg2, typename TArg3, typename TArg4 >
        inline void Constructor::operator()(void* obj, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4) const {
            RS_ASSERTF(argumentTypes_.size() == 4, "Provided argument count is incorrect. Should be: %i", argumentTypes_.size());
            RS_ASSERT(argumentTypes_[0] == ConvertToQualifiedType< TArg1 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[1] == ConvertToQualifiedType< TArg2 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[2] == ConvertToQualifiedType< TArg3 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[3] == ConvertToQualifiedType< TArg4 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(obj != 0, "'obj' is null");
            typedef void* (*FunctionType) (void*, TArg1, TArg2, TArg3, TArg4);
            FunctionType func = reinterpret_cast< FunctionType >(creatorFunc_.fxn_ptr);
            (*func)(obj, arg1, arg2, arg3, arg4);
        }

        template< typename TArg1, typename TArg2, typename TArg3, typename TArg4, typename TArg5 >
        inline void* Constructor::operator()(TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5) const {
            RS_ASSERTF(argumentTypes_.size() == 5, "Provided argument count is incorrect. Should be: %i", argumentTypes_.size());
            RS_ASSERT(argumentTypes_[0] == ConvertToQualifiedType< TArg1 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[1] == ConvertToQualifiedType< TArg2 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[2] == ConvertToQualifiedType< TArg3 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[3] == ConvertToQualifiedType< TArg4 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[4] == ConvertToQualifiedType< TArg5>::kQualifiedType, "Wrong argument type");
            typedef void* (*FunctionType) (void*, TArg1, TArg2, TArg3, TArg4, TArg5);
            FunctionType func = reinterpret_cast< FunctionType >(creatorFunc_.fxn_ptr);
            void* obj = malloc(classSize_);
            return (*func)(obj, arg1, arg2, arg3, arg4, arg5);
        }

        template< typename TArg1, typename TArg2, typename TArg3, typename TArg4, typename TArg5 >
        inline void Constructor::operator()(void* obj, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5) const {
            RS_ASSERTF(argumentTypes_.size() == 5, "Provided argument count is incorrect. Should be: %i", argumentTypes_.size());
            RS_ASSERT(argumentTypes_[0] == ConvertToQualifiedType< TArg1 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[1] == ConvertToQualifiedType< TArg2 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[2] == ConvertToQualifiedType< TArg3 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[3] == ConvertToQualifiedType< TArg4 >::kQualifiedType, "Wrong argument type");
            RS_ASSERT(argumentTypes_[4] == ConvertToQualifiedType< TArg5>::kQualifiedType, "Wrong argument type");
            RS_ASSERT(obj != 0, "'obj' is null");
            typedef void* (*FunctionType) (void*, TArg1, TArg2, TArg3, TArg4, TArg5);
            FunctionType func = reinterpret_cast< FunctionType >(creatorFunc_.fxn_ptr);
            (*func)(obj, arg1, arg2, arg3, arg4, arg5);
        }
        
        inline Type Constructor::GetArgumentType(UInt32 i) const {
            RS_ASSERT(i < argumentTypes_.size(), "requested argument index, 'i', is out of range");
            return Type(argumentTypes_[i]);
        }
        
        inline UInt64 Constructor::GetRawArgumentType(UInt32 i) const {
            RS_ASSERT(i < argumentTypes_.size(), "requested argument index, 'i', is out of range");
            return argumentTypes_[i];
        }
        
        inline RsSize Constructor::GetArgumentCount() const {
            return argumentTypes_.size();
        }
    }
}

#endif // RS_REFLECTION_CONSTRUCTOR_IPP
